using System;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Think.Problems;

namespace Think.Test.Problems
{
    [TestClass]
    public class DeckTest
    {
        [TestMethod]
        public void ConstructDeck()
        {
            // Execute
            new Deck();
        }

        [TestMethod]
        public void ConstructDeckWithSeed()
        {
            // Execute
            new Deck(0);
        }

        [TestMethod]
        public void ConstructWithExistingCards()
        {
            // Execute
            var deck = new Deck(new[] {"AH", "AC", "AS", "AD"});

            // Assert
            Assert.AreEqual(4, deck.Count);
            var cards = deck.Draw(4);
            CollectionAssert.AreEqual(new[] { "AH", "AC", "AS", "AD" }, cards);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ConstructWithNullCards()
        {
            // Execute
            var deck = new Deck(a_cards: null);
        }

        [TestMethod]
        public void Draw1CardFromUnsortedDeck()
        {
            // Setup
            var deck = new Deck();

            // Execute
            var card = deck.Draw();

            // Assert
            Assert.AreEqual("AH", card);
        }

        [TestMethod]
        public void Draw1CardFromTwoDecksWithSameSeed()
        {
            // Setup
            var deck1 = new Deck(0);
            var deck2 = new Deck(0);

            // Execute
            var card1 = deck1.Draw();
            var card2 = deck2.Draw();

            // Assert
            Assert.AreNotEqual("AH", card1);
            Assert.AreEqual(card1, card2);
        }

        [TestMethod]
        public void Draw3CardFromUnsortedDeck()
        {
            // Setup 
            var deck = new Deck();

            // Execute
            var cards = deck.Draw(3).ToList();

            // Assert
            Assert.AreEqual(3, cards.Count);
            CollectionAssert.AreEqual(new [] { "AH", "2H", "3H" }, cards);
        }

        [TestMethod]
        public void Draw3CardsFromTwoDecksWithSameSeed()
        {
            // Setup
            var deck1 = new Deck(0);
            var deck2 = new Deck(0);

            // Execute
            var cards1 = deck1.Draw(3).ToList();
            var cards2 = deck2.Draw(3).ToList();

            // Assert
            CollectionAssert.AreEqual(cards1.ToList(), cards2.ToList());
        }

        [TestMethod]
        public void Draw3CardsAndCheckCount()
        {
            // Setup
            var deck = new Deck();

            // Execute
            deck.Draw(3);

            // Assert
            Assert.AreEqual(49, deck.Count);
        }

        [TestMethod]
        public void DrawTooManyCards()
        {
            // Setup
            var deck = new Deck();

            // Execute
            var cards = deck.Draw(60);
            
            // Assert
            Assert.AreEqual(52, cards.Length);
            Assert.AreEqual(0, deck.Count);
        }

        [TestMethod]
        public void DrawCardFromEmptyDeck()
        {
            // Setup
            var deck = new Deck();

            // Execute
            deck.Draw(56);
            var card = deck.Draw();

            // Assert
            Assert.IsNull(card);
        }

        [TestMethod]
        public void ReturnDrawnCardToTheBottomOfTheDeck()
        {
            // Setup
            var deck = new Deck();
            
            // Execute
            var card = deck.Draw();
            deck.ReturnToBottom(card);

            deck.Draw(51);
            var result = deck.Draw();

            // Assert
            Assert.AreEqual(card, result);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentNullException))]
        public void ReturnNullToTheBottomOfTheDeck()
        {
            // Setup
            var deck = new Deck();

            // Execute
            deck.ReturnToBottom(a_card: null);
        }

        [TestMethod]
        public void CloneDeck()
        {
            // Setup
            var deck = new Deck();

            // Execute
            var result = deck.Clone();

            // Assert
            Assert.AreNotSame(deck, result);
            Assert.AreEqual(deck.Count, result.Count);
            var count = deck.Count;
            for (int i = 0; i < count; i++)
                Assert.AreEqual(deck.Draw(), result.Draw());
        }
    }
}
